//这个函数是一个调度器，这个调度器接受一系列的预测器，并且接受一段的历史输入序列，从而可以得到预测的结果

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pageread-predictor.h"
#include "readcache-with-LRU.h"
#include "Noah-predictor.h"
#include "mode-judge.h"
#include "markov-predictor.h"

//每一行的最大字节数，同时也是行缓冲区的大小
#define MAX_LINE_SIZE 1024

//预测深度的最大值
#define MAX_PREDICT_DEEP 32


//这里创建一个预测的管理器，我们使用这个管理器来进行预测
void predictor_manager(){
    //从文件中一个个读入
    FILE *fp_read = NULL;
    char line[MAX_LINE_SIZE];
    long access_block_count;
    long* predict_seq;
    long* predict_seq2;
    long* predict_seq3;
    int size;
    int size2;
    int size3;
    int i;

    //这里加入一个读缓存
    all_read_cache_t read_caches;
    //初始化读缓存
    init_readcache_meta(&read_caches);

    //初始化一个函数来看看缓存命中率
    long all_count_of_block = 0;
    long hit_count_of_block = 0;
    long noah_predict_time = 0;
    long stable_predictor_time = 0;

    //这里可以记录上一次访问的节点
    long last_access_block = -1;

    //初始化一个预测序列
    predict_seq = (long*)malloc(MAX_PREDICT_DEEP*sizeof(long));
    //初始化给noah用的第二个序列
    predict_seq2 = (long*)malloc(MAX_PREDICT_DEEP*sizeof(long));
    predict_seq3 = (long*)malloc(MAX_PREDICT_DEEP*sizeof(long));
    
    printf("开始运行预测\n");

    if((fp_read = fopen("resource/block_count_proj.csv", "r"))!=NULL){
        printf("开始读取访问的历史记录\n");

        //初始化noah
        Noah_predictor_t noah;
        Noah_predictor_t stable_noah;
        page_predictor_t page_predictor_1;
        Noah_predictor_init(&noah, 1);
        Noah_predictor_init(&stable_noah, 12);
        page_init(&page_predictor_1);

        //初始化mode_judger
        mode_judge_t mode_judger;
        init_mode_judge(&mode_judger);

        //初始化markov
        markov_block_predictor_t markov;
        init_markov_block_meta(&markov);

        while(fgets(line, MAX_LINE_SIZE, fp_read)){
            //记录读进来的块
            all_count_of_block++;

            //将一行的内容转化为块号传入
            access_block_count = atol(line);

            //这里记录一下page预测器的命中情况
            update_when_access_page_cache(&mode_judger, access_block_count);

            //看看这个块是不是命中的
            //哈希一下
            int barrel_num = access_block_count % MAX_READ_CACHE_NUM;
            if(access_read_cache(&(read_caches.read_cache_arr[barrel_num]), access_block_count) != NULL){
                hit_count_of_block++;
            }
            
            //在外部将传入的预测序列初始化
            memset(predict_seq, 0, MAX_PREDICT_DEEP*sizeof(long));
            memset(predict_seq2, 0 , MAX_PREDICT_DEEP*sizeof(long));
            memset(predict_seq3, 0 , MAX_PREDICT_DEEP*sizeof(long));

            page_predictor(&page_predictor_1, access_block_count, predict_seq, &size);
            noah_predictor(&noah, access_block_count, predict_seq2, &size2);
            noah_predictor(&stable_noah, access_block_count, predict_seq3, &size3);

            // if(size2 > 0){
            //     // printf("从noah中预测的内容access_count%ld:", access_block_count);
            //     // for(int j = 0 ; j < size2 ; j++){
            //     //     printf("%ld,", predict_seq2[j]);
            //     // }
            //     // printf("\n");
            //     noah_predict_time++;
            // }

            // if(size3 > 0){
            //     stable_predictor_time++;
            // }

            //如果是新读入缓存的也要记一次访问
            
            add_cache_meta(&read_caches, access_block_count);
            
            access_read_cache(&(read_caches.read_cache_arr[barrel_num]), access_block_count);
            
            // if(mode_judger.page_err_rate >= 1){
            // printf("当前page预测器错误率:%d%%\n", mode_judger.page_err_rate);
            // }
            

            //这里看看预测成功率
            int judge = 0;
            if(size2 == 0 && get_mode_of_predictor(&mode_judger) == 1){
                printf("单纯使用新技法\n");
                //这里可以单纯使用page的预测方法
                for(i = 0; i < size; i++){
                    add_cache_meta(&read_caches, access_block_count);
                    judge = 1;
                    update_and_judge_mode(&mode_judger, predict_seq[i]);
                }
            }

            //单纯记录连续记录的成功率
            // for(i = 0; i < size; i++) {
            //     update_and_judge_mode(&mode_judger, predict_seq[i]);
            // }

            

            for(i = 0;  i < size2 && i < size; i++){
                //当两个预测器预测地一样的时候才会写入
                if(predict_seq[i] == predict_seq2[i]){
                    add_cache_meta(&read_caches, access_block_count);
                    judge = 1;
                    // if(i == 0){
                    //     update_and_judge_mode(&mode_judger, predict_seq[i]);
                    // }
                }else if(i < size3 && predict_seq2[i] == predict_seq3[i]){
                    add_cache_meta(&read_caches, access_block_count);
                    judge = 1;
                }
            }

            if(judge == 1){
                noah_predict_time++;
            }
            
            // if(size > 0){
            //     noah_predict_time++;

            //     for(i = 0;  i < size; i++){
            //         //当两个预测器预测地一样的时候才会写入
            //         add_cache_meta(&read_caches, predict_seq[i]);    
            //     }
            // }

            //打印一下预测到的东西
            // printf("预测:");
            // for(i = 0;  i < size; i++){
            //     printf(" %ld", predict_seq[i]);
            // }

            // printf("\n");
            last_access_block = access_block_count;
        }
        
    }else{
        printf("文件开启失败\n");
        return;
    }

    //关闭
    printf("读取完毕\n");
    fclose(fp_read);
    fp_read = NULL;
    
    //计算缓存命中率
    int hit_rate = hit_count_of_block  * 100 / all_count_of_block;

    //预测错误率
    //这里整理一下预测错误率
    long err_cache;
    for(int i = 0 ; i < MAX_READ_CACHE_NUM; i++){
        err_cache = err_cache + read_caches.read_cache_arr[i].page_cache_have_not_access;

        //将已有的数据拿出做一个分析计数
        read_cache_t* each_read_cache = &(read_caches.read_cache_arr[i]);

        read_cache_meta_t* scan_item = each_read_cache->head->next;

        while(scan_item != each_read_cache->head){
            if(scan_item->have_accessed == 0){
                err_cache++;
            }
            scan_item = scan_item->next;
        }
    }
    long all_del_cache;
    for(int i = 0 ; i < MAX_READ_CACHE_NUM; i++){
        all_del_cache = all_del_cache + read_caches.read_cache_arr[i].page_cache_del;

        read_cache_t* each_read_cache = &(read_caches.read_cache_arr[i]);

        read_cache_meta_t* scan_item = each_read_cache->head->next;

        while(scan_item != each_read_cache->head){
            
            all_del_cache++;
            
            scan_item = scan_item->next;
        }
    }

    printf("所有弹出的缓存%ld\n", all_del_cache);
    printf("错误的次数%ld\n", err_cache);
    // printf("noah预测的触发次数%ld\n", noah_predict_time);
    // printf("稳定预测的触发次数%ld\n", stable_predictor_time);
    printf("总访问次数%ld\n", all_count_of_block);
    printf("缓冲命中率:%d%%\n", hit_rate);

    int err_rate = err_cache * 100 / all_del_cache;
    
    int predict_rate = noah_predict_time * 100 / all_count_of_block;
    printf("预测触发率:%d%%\n", predict_rate);
    printf("预测错误率:%d%%\n", err_rate);
}

int main(){
    predictor_manager();
}